import tkinter as tk
from tkinter import ttk, messagebox
import time
import os
from log_manager import LogManager

class UIManager:
    def __init__(self, root):
        self.root = root
        self.file_lists = {}
        self.status_widgets = {}
        self.progress_var = tk.DoubleVar()
        
    def create_main_window(self):
        """创建主窗口"""
        self.root.title("Serial Disk")
        self.root.geometry("1200x600")
        
        # 创建菜单栏
        self._create_menu()
        
        # 创建主区域
        self._create_main_area()
        
        # 创建状态栏
        self._create_status_bar()
        
    def update_file_list(self, is_local, files):
        """更新文件列表"""
        list_widget = self.file_lists['local' if is_local else 'device']
        list_widget.delete(*list_widget.get_children())
        
        for file_info in files:
            icon = "📁 " if file_info['is_directory'] else "📄 "
            list_widget.insert('', 'end',
                text=f"{icon}{file_info['name']}",
                values=(
                    self._format_size(file_info['size']),
                    self._format_time(file_info['mtime'])
                ),
                tags=(str(file_info),)
            )
            
    def update_progress(self, progress, status):
        """更新进度
        
        Args:
            progress: 进度值（0-100）
            status: 状态信息
        """
        try:
            # 显示进度条
            self.show_progress_bar()    
            
            # 更新进度
            self.progress_var.set(progress)
            
            # 更新状态文本
            self.status_widgets['status'].config(text=status)
            
            # 确保更新显示
            self.root.update_idletasks()
            
        except Exception as e:
            LogManager.error(f"更新进度失败: {str(e)}")
            
    def show_progress_bar(self):
        """显示进度条"""
        if not hasattr(self, 'progress_bar') or not self.progress_bar:
            return
            
        try:
            # 调整状态文本宽度
            self.status_widgets['status'].pack(side='left', padx=(5, 2), fill=None, expand=False)
            
            # 显示进度条
            self.progress_bar.pack(side='left', fill='x', expand=True, padx=2)
            
            # 显示状态标签
            self.status_label.pack(side='left', padx=(2, 5))
            
            # 设置传输状态
            self.set_transfer_state(True)
            
        except Exception as e:
            LogManager.error(f"显示进度条失败: {str(e)}")
            
    def hide_progress_bar(self):
        """隐藏进度条"""
        if not hasattr(self, 'progress_bar') or not self.progress_bar:
            return
            
        try:
            # 隐藏进度条和状态标签
            self.progress_bar.pack_forget()
            self.status_label.pack_forget()
            
            # 恢复状态文本显示
            self.status_widgets['status'].pack(side='left', fill='x', expand=True, padx=5)
            
            # 重置状态
            self.progress_bar['value'] = 0
            self.status_label.config(text="")
            
            # 恢复控件状态
            self.set_transfer_state(False)
            
        except Exception as e:
            LogManager.error(f"隐藏进度条失败: {str(e)}")
            
    def _parse_size(self, size_str):
        """解析大小字符串为字节数
        
        Args:
            size_str: 大小字符串，如 "1.5 MB"、"500 KB" 等
            
        Returns:
            int: 字节数
        """
        try:
            size, unit = size_str.strip().split(' ')
            size = float(size)
            if unit == 'B':
                return int(size)
            elif unit == 'KB':
                return int(size * 1024)
            elif unit == 'MB':
                return int(size * 1024 * 1024)
            elif unit == 'GB':
                return int(size * 1024 * 1024 * 1024)
            return 0
        except:
            return 0
        
    def add_log(self, message):
        """添加日志"""
        self.status_widgets['log'].config(state='normal')
        self.status_widgets['log'].insert('end', f"{message}\n")
        self.status_widgets['log'].see('end')
        self.status_widgets['log'].config(state='disabled')
        
    def show_error(self, message):
        """显示错误"""
        tk.messagebox.showerror("错误", message) 
        
    def _create_menu(self):
        """创建菜单栏"""
        menubar = tk.Menu(self.root)
        self.root.config(menu=menubar)
        
        # 文件菜单
        file_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="文件", menu=file_menu)
        file_menu.add_command(label="退出", command=self.root.quit)
        
        # 帮助菜单
        help_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="帮助", menu=help_menu)
        help_menu.add_command(label="关于", command=self._show_about)
        
    def _create_main_area(self):
        """创建主区域"""
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill='both', expand=True, padx=5, pady=5)
        
        # 创建串口控制区域
        self._create_port_controls(main_frame)
        
        # 创建文件列表区域
        self._create_file_lists(main_frame)
        
        # 创建日志区域
        self._create_log_area(main_frame)
        
    def _create_status_bar(self):
        """创建状态栏"""
        status_frame = ttk.Frame(self.root)
        status_frame.pack(fill='x', side='bottom', pady=2, padx=5)
        
        # 创建状态文本标签（左侧）
        self.status_widgets['status'] = ttk.Label(
            status_frame,
            text="就绪",
            relief=tk.SUNKEN,
            anchor='w',
            padding=(5, 2)
        )
        self.status_widgets['status'].pack(side='left', fill='x', expand=True, padx=5)
        
        # 创建进度条（中间）
        self.progress_bar = ttk.Progressbar(
            status_frame,
            variable=self.progress_var,
            maximum=100,
            mode='determinate'
        )
        # 初始时不显示进度条
        
        # 创建状态标签（右侧）
        self.status_label = ttk.Label(
            status_frame,
            text="",
            anchor='e'
        )
        # 初始时不显示状态标签
        
    def _create_port_controls(self, parent):
        """创建串口控制区域"""
        port_frame = ttk.Frame(parent)
        port_frame.pack(fill='x', padx=5, pady=5)
        
        # 左侧串口控件
        port_controls = ttk.Frame(port_frame)
        port_controls.pack(side='left')
        
        ttk.Label(port_controls, text="串口:").pack(side='left', padx=(0, 5))
        self.port_combobox = ttk.Combobox(port_controls, width=15)
        self.port_combobox.pack(side='left', padx=(0, 5))
        
        self.refresh_button = ttk.Button(port_controls, text="刷新")
        self.refresh_button.pack(side='left', padx=(0, 5))
        
        self.connect_button = ttk.Button(port_controls, text="连接")
        self.connect_button.pack(side='left')
        
        # 右侧按钮区域
        right_buttons = ttk.Frame(port_frame)
        right_buttons.pack(side='right')
        
        self.device_info_btn = ttk.Button(right_buttons, text="设备信息")
        self.device_info_btn.pack(side='left', padx=5)
        self.device_info_btn.state(['disabled'])
        
        self.disk_info_btn = ttk.Button(right_buttons, text="U盘信息")
        self.disk_info_btn.pack(side='left', padx=5)
        self.disk_info_btn.state(['disabled'])
        
        self.cancel_button = ttk.Button(right_buttons, text="取消传输")
        self.cancel_button.pack(side='left', padx=5)
        self.cancel_button.state(['disabled'])

    def _create_file_lists(self, parent):
        """创建文件列表区域"""
        # 创建文件列表框架
        lists_frame = ttk.Frame(parent)
        lists_frame.pack(fill='both', expand=True, padx=5, pady=5)
        
        # 本地文件区域
        local_frame = ttk.LabelFrame(lists_frame, text="本地文件")
        local_frame.pack(side='left', fill='both', expand=True)
        
        # 本地路径输入区域
        local_path_frame = ttk.Frame(local_frame)
        local_path_frame.pack(fill='x', padx=5, pady=2)
        
        ttk.Label(local_path_frame, text="路径:").pack(side='left', padx=(0, 5))
        self.local_path_var = tk.StringVar()
        self.local_path_entry = ttk.Entry(local_path_frame, textvariable=self.local_path_var)
        self.local_path_entry.pack(side='left', fill='x', expand=True)
        
        # 添加上级目录按钮
        ttk.Button(local_path_frame, text="↑", width=3,
                  command=lambda: self._go_parent_dir('local')).pack(side='left', padx=(5,0))
        
        # 本地文件列表
        self.file_lists['local'] = ttk.Treeview(
            local_frame,
            columns=('size', 'mtime'),
            selectmode='browse'
        )
        self._setup_treeview(self.file_lists['local'])
        
        # 设备文件区域
        device_frame = ttk.LabelFrame(lists_frame, text="设备文件")
        device_frame.pack(side='right', fill='both', expand=True)
        
        # 设备路径输入区域
        device_path_frame = ttk.Frame(device_frame)
        device_path_frame.pack(fill='x', padx=5, pady=2)
        
        ttk.Label(device_path_frame, text="路径:").pack(side='left', padx=(0, 5))
        self.device_path_var = tk.StringVar()
        self.device_path_entry = ttk.Entry(device_path_frame, textvariable=self.device_path_var)
        self.device_path_entry.pack(side='left', fill='x', expand=True)
        
        # 添加上级目录按钮
        ttk.Button(device_path_frame, text="↑", width=3,
                  command=lambda: self._go_parent_dir('device')).pack(side='left', padx=(5,0))
        
        # 设备文件列表
        self.file_lists['device'] = ttk.Treeview(
            device_frame,
            columns=('size', 'mtime'),
            selectmode='browse'
        )
        self._setup_treeview(self.file_lists['device'])

    def _create_log_area(self, parent):
        """创建日志区域"""
        log_frame = ttk.LabelFrame(parent, text="日志")
        log_frame.pack(fill='x', padx=5, pady=5)
        
        # 创建日志文本框和滚动条
        scrollbar = ttk.Scrollbar(log_frame)
        scrollbar.pack(side='right', fill='y')
        
        self.status_widgets['log'] = tk.Text(
            log_frame,
            height=6,
            yscrollcommand=scrollbar.set,
            wrap=tk.WORD
        )
        self.status_widgets['log'].pack(fill='x', expand=True)
        scrollbar.config(command=self.status_widgets['log'].yview)
        
        # 设置只读
        self.status_widgets['log'].config(state='disabled')
        
    def _format_size(self, size):
        """格式化文件大小"""
        if size < 1024:
            return f"{size} B"
        elif size < 1024 * 1024:
            return f"{size/1024:.1f} KB"
        else:
            return f"{size/(1024*1024):.1f} MB"
            
    def _format_speed(self, bytes_per_second):
        """格式化传输速度"""
        if bytes_per_second < 1024:
            return f"{bytes_per_second:.1f} B/s"
        elif bytes_per_second < 1024 * 1024:
            return f"{bytes_per_second/1024:.1f} KB/s"
        else:
            return f"{bytes_per_second/(1024*1024):.1f} MB/s"

    def _format_time(self, timestamp):
        """格式化时间戳
        
        Args:
            timestamp: Unix时间戳
            
        Returns:
            str: 格式化后的时间字符串，格式为 YYYY-MM-DD HH:mm:ss
        """
        try:
            # 将时间戳转换为本地时间
            local_time = time.localtime(timestamp)
            # 格式化为易读的字符串
            return time.strftime('%Y-%m-%d %H:%M:%S', local_time)
        except Exception as e:
            LogManager.error(f"时间格式化失败: {str(e)}")
            return "未知时间"
        
    def _local_path_up(self):
        """本地路径向上导航"""
        current = self.local_path_var.get()
        parent = os.path.dirname(current)
        if parent and parent != current:
            self.local_path_var.set(parent)
            
    def _device_path_up(self):
        """设备路径向上导航"""
        current = self.device_path_var.get()
        parent = os.path.dirname(current)
        if parent and parent != current:
            self.device_path_var.set(parent)
            
    def bind_button(self, button_name, callback):
        """绑定按钮事件"""
        if button_name == 'connect':
            self.connect_button.config(command=callback)
        elif button_name == 'refresh':
            self.refresh_button.config(command=callback)
        elif button_name == 'device_info':
            self.device_info_btn.config(command=callback)
        elif button_name == 'disk_info':
            self.disk_info_btn.config(command=callback)
        elif button_name == 'cancel':
            self.cancel_button.config(command=callback)
            
    def bind_list(self, list_name, event, callback):
        """绑定列表事件"""
        self.file_lists[list_name].bind(event, callback)
        
    def get_port(self):
        """获取当前选择的串口
        
        Returns:
            str: 当前选择的串口名称，如果未选择则返回None
        """
        try:
            return self.port_combobox.get()
        except Exception as e:
            LogManager.error(f"获取串口选择失败: {str(e)}")
            return None
        
    def set_connected_state(self, is_connected):
        """设置连接状态"""
        if is_connected:
            self.connect_button.config(text="断开")
            self.device_info_btn.state(['!disabled'])
            self.disk_info_btn.state(['!disabled'])
        else:
            self.connect_button.config(text="连接")
            self.device_info_btn.state(['disabled'])
            self.disk_info_btn.state(['disabled'])
            self.cancel_button.state(['disabled'])
            
    def get_selected_item(self, list_name):
        """获取选中的文件信息"""
        list_widget = self.file_lists[list_name]
        selection = list_widget.selection()
        if not selection:
            return None
        
        # 获取选中的item
        item_id = selection[0]
        item = list_widget.item(item_id)
        
        # 从tags中获取完整的文件信息
        try:
            file_info = eval(item['tags'][0])  # 将字符串转回字典
            return file_info
        except (IndexError, SyntaxError):
            # 如果tags解析失败，返回None
            return None
        
    def _show_about(self):
        """显示关于对话框"""
        tk.messagebox.showinfo(
            "关于",
            "Serial Disk v1.0\n"
            "一个简单的串口文件传输工具\n"
            "作者: Weyne Chen"
        ) 
        
    def update_port_list(self, ports):
        """更新串口列表
        
        Args:
            ports (list): 可用串口列表
        """
        try:
            # 清空当前列表
            self.port_combobox['values'] = []
            
            if not ports:
                LogManager.warning("未找到可用串口")
                return
            
            # 更新串口列表
            self.port_combobox['values'] = ports
            
            # 如果有串口，默认选择第一个
            if len(ports) > 0:
                self.port_combobox.set(ports[0])
            
        except Exception as e:
            LogManager.error(f"更新串口列表失败: {str(e)}") 
        
    def set_port(self, port):
        """设置当前选择的串口
        
        Args:
            port (str): 要选择的串口名称
        """
        try:
            # 检查端口是否在可选列表中
            if port in self.port_combobox['values']:
                self.port_combobox.set(port)
            else:
                LogManager.warning(f"串口 {port} 不在可用列表中")
                
        except Exception as e:
            LogManager.error(f"设置串口选择失败: {str(e)}") 
        
    def _setup_treeview(self, treeview):
        """设置树形视图的列和样式
        
        Args:
            treeview: 要设置的树形视图控件
        """
        # 设置列标题
        treeview.heading('#0', text='文件名')
        treeview.heading('size', text='大小')
        treeview.heading('mtime', text='修改时间')
        
        # 设置列宽度
        treeview.column('#0', width=300, minwidth=200)
        treeview.column('size', width=100, minwidth=80)
        treeview.column('mtime', width=150, minwidth=120)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(treeview.master, orient='vertical', command=treeview.yview)
        scrollbar.pack(side='right', fill='y')
        treeview.configure(yscrollcommand=scrollbar.set)
        
        # 设置选择模式
        treeview.configure(selectmode='browse')
        
        # 设置样式
        style = ttk.Style()
        style.configure('Treeview', rowheight=25)
        
        # 绑定排序事件
        for col in ('size', 'mtime'):
            treeview.heading(col, command=lambda c=col: self._treeview_sort_column(treeview, c))
            
        # 打包树形视图
        treeview.pack(side='left', fill='both', expand=True)
        
    def _treeview_sort_column(self, treeview, col):
        """对树形视图的列进行排序
        
        Args:
            treeview: 要排序的树形视图控件
            col: 要排序的列名
        """
        # 获取所有项目
        items = [(treeview.set(item, col), item) for item in treeview.get_children('')]
        
        # 根据类型进行排序
        if col == 'size':
            # 按文件大小排序（使用tags中存储的原始大小）
            items = [(int(treeview.item(item)['tags'][0]), item) for item in treeview.get_children('')]
        else:
            # 按字符串排序
            items.sort()
            
        # 重新排列项目
        for index, (val, item) in enumerate(items):
            treeview.move(item, '', index)
            
        # 切换排序方向
        treeview.heading(col, command=lambda: self._treeview_sort_column(treeview, col)) 
        
    def _go_parent_dir(self, list_name):
        """转到上级目录"""
        try:
            if list_name == 'local':
                current_path = self.local_path_var.get()
                parent_path = os.path.dirname(current_path)
                if os.path.exists(parent_path):
                    self.local_path_var.set(parent_path)
                    # 触发路径变更事件
                    self.local_path_entry.event_generate('<Return>')
                    # 如果Return事件没触发，直接调用回调
                    self.local_path_entry.event_generate('<FocusOut>')
            elif list_name == 'device':
                current_path = self.device_path_var.get()
                # 如果已经在/usb目录，不允许再往上
                if current_path == '/usb':
                    return
                parent_path = os.path.dirname(current_path)
                # 确保不会超出/usb目录
                if parent_path == '/':
                    parent_path = '/usb'
                LogManager.debug(f"设备路径上级目录: {parent_path}")
                self.device_path_var.set(parent_path)
                # 触发路径变更事件
                self.device_path_entry.event_generate('<Return>')
                # 如果Return事件没触发，直接调用回调
                self.device_path_entry.event_generate('<FocusOut>')
        except Exception as e:
            LogManager.error(f"转到上级目录失败: {str(e)}")

    def set_path(self, list_name, path):
        """设置路径
        
        Args:
            list_name: 列表名称('local'或'device')
            path: 要设置的路径
        """
        try:
            if list_name == 'local':
                self.local_path_var.set(path)
            elif list_name == 'device':
                self.device_path_var.set(path)
            else:
                raise ValueError(f"未知的列表名称: {list_name}")
                
        except Exception as e:
            LogManager.error(f"设置路径失败: {str(e)}")

    def get_path(self, list_name):
        """获取当前路径
        
        Args:
            list_name: 列表名称('local'或'device')
            
        Returns:
            str: 当前路径
        """
        try:
            if list_name == 'local':
                return self.local_path_var.get()
            elif list_name == 'device':
                return self.device_path_var.get()
            else:
                raise ValueError(f"未知的列表名称: {list_name}")
                
        except Exception as e:
            LogManager.error(f"获取路径失败: {str(e)}")
            return None

    def bind_path_change(self, list_name, callback):
        """绑定路径变更事件"""
        if list_name == 'local':
            LogManager.debug("绑定本地路径变更事件")
            # 绑定回车事件
            self.local_path_entry.bind('<Return>', lambda e: callback(self.local_path_var.get()))
            # 同时绑定FocusOut事件，以防Return事件不触发
            self.local_path_entry.bind('<FocusOut>', lambda e: callback(self.local_path_var.get()))
        elif list_name == 'device':
            LogManager.debug("绑定设备路径变更事件")
            # 绑定回车事件
            self.device_path_entry.bind('<Return>', lambda e: callback(self.device_path_var.get()))
            # 同时绑定FocusOut事件，以防Return事件不触发
            self.device_path_entry.bind('<FocusOut>', lambda e: callback(self.device_path_var.get())) 

    def set_transfer_state(self, is_transferring):
        """设置传输状态，控制UI元素的启用/禁用
        
        Args:
            is_transferring: 是否正在传输
        """
        try:
            if is_transferring:
                # 禁用可能影响设备操作的控件
                self.connect_button.state(['disabled'])
                self.device_info_btn.state(['disabled'])
                self.disk_info_btn.state(['disabled'])
                self.file_lists['device'].state(['disabled'])
                self.device_path_entry.state(['disabled'])
                # 启用取消按钮
                self.cancel_button.state(['!disabled'])
            else:
                # 恢复控件状态
                self.connect_button.state(['!disabled'])
                self.device_info_btn.state(['!disabled'])
                self.disk_info_btn.state(['!disabled'])
                self.file_lists['device'].state(['!disabled'])
                self.device_path_entry.state(['!disabled'])
                # 禁用取消按钮
                self.cancel_button.state(['disabled'])
                
        except Exception as e:
            LogManager.error(f"设置传输状态失败: {str(e)}")
            
    def _format_time_duration(self, seconds):
        """格式化时间间隔
        
        Args:
            seconds: 秒数
            
        Returns:
            str: 格式化后的时间字符串
        """
        if seconds < 60:
            return f"{seconds:.1f}秒"
        elif seconds < 3600:
            minutes = seconds / 60
            return f"{minutes:.1f}分钟"
        else:
            hours = seconds / 3600
            return f"{hours:.1f}小时" 